﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using MessageSystem.Managed.Rpc;

namespace RpcGenerator
{
    class MessageDescriptionGenerator
    {
        public List<MessageDescription> Generate(Type type)
        {
            List<MessageDescription> result = new List<MessageDescription>();
            foreach (MethodDescription m in GetMethods(type))
            {
                MessageDescription request = GetRequestMessage(m);
                request.ObjectMaskSize = GetObjectMaskSize(request.ObjectCount);
                request.Type = type;
                result.Add(request);
                MessageDescription response = GetResponseMessage(m);
                if (null != response)
                {
                    request.Response = response;
                    response.ObjectMaskSize = GetObjectMaskSize(response.ObjectCount);
                    response.Type = type;
                    result.Add(response);
                }
                GetSharedField(request);
            }
            return result;
        }

        private List<MethodDescription> GetMethods(Type type)
        {
            List<MethodDescription> result = new List<MethodDescription>();
            foreach (MethodInfo mi in type.GetMethods())
            {
                MethodDescription desc = new MethodDescription();
                desc.Interface = type;
                desc.Method = mi;
                result.Add(desc);
            }
            return result;
        }

        private MessageDescription GetRequestMessage(MethodDescription method)
        {
            MessageDescription result = new MessageDescription();
            result.Method = method;
            result.IsRequest = true;
            result.Name = method.Interface.Name + "_" + method.Method.Name + "_Request";
            result.MessageId = GetMessageId(method);
            result.MethodId = (Int16)RpcControl.GetHashCode(method.Method.Name);
            result.Fields = new List<MessageFieldDescription>();
            if(method.Method.GetCustomAttributes(typeof(RpcMethodAttribute), false).Length > 0)
            {
                RpcMethodAttribute rma = method.Method.GetCustomAttributes(typeof(RpcMethodAttribute), false)[0] as RpcMethodAttribute;
                result.NeedEncrypt = rma.NeedEncrypt;
                result.SupportBroadcast = rma.SupportBroadcast;
                result.AsynMethod = rma.AsynMethod;
            }            
            foreach (ParameterInfo pi in method.Method.GetParameters())
            {
                MessageFieldDescription mfd = new MessageFieldDescription();
                mfd.Name = pi.Name;
                mfd.Type = pi.ParameterType;                
                if (mfd.IsGenericType())
                {
                    mfd.GenericTypeName = string.Format("List<{0}>", mfd.GetGenericType().Name);                                        
                }

                if (!mfd.IsPrimitiveType())
                {
                    mfd.ObjectIndex = ++result.ObjectCount;
                }
                CreateMembers(mfd);
                result.Fields.Add(mfd);
            }
            return result;
        }  

        private MessageDescription GetResponseMessage(MethodDescription method)
        {
            MessageDescription result = null;
            if (!string.Equals(method.Method.ReturnType.FullName, "System.Void"))
            {
                result = new MessageDescription();
                result.Method = method;
                result.IsRequest = false;
                result.Name = method.Interface.Name + "_" + method.Method.Name + "_Response";
                result.MessageId = GetMessageId(method);
                result.MethodId = (Int16)RpcControl.GetHashCode(method.Method.Name);
                result.Fields = new List<MessageFieldDescription>();
                if (method.Method.GetCustomAttributes(typeof(RpcMethodAttribute), false).Length > 0)
                {
                    RpcMethodAttribute rma = method.Method.GetCustomAttributes(typeof(RpcMethodAttribute), false)[0] as RpcMethodAttribute;
                    result.NeedEncrypt = rma.NeedEncrypt;
                    result.SupportBroadcast = rma.SupportBroadcast;
                    result.AsynMethod = rma.AsynMethod;
                }      

                MessageFieldDescription mfd = new MessageFieldDescription();
                mfd.Name = "returnValue";
                mfd.Type = method.Method.ReturnType;
                if (mfd.IsGenericType())
                {
                    mfd.GenericTypeName = string.Format("List<{0}>", mfd.GetGenericType().Name);
                }

                if (!mfd.IsPrimitiveType())
                {
                    mfd.ObjectIndex = ++result.ObjectCount;
                }
                CreateMembers(mfd);
                result.Fields.Add(mfd);
            }
            return result;
        }

        private void CreateMembers(MessageFieldDescription mfd)
        {
            mfd.ObjectCount = 0;
            mfd.Members = new List<MessageFieldMemberDescription>();

            if (mfd.GetMessageFieldType() != MessageFieldType.ClientType)
            {
                return;
            }

            foreach (PropertyInfo pi in mfd.Type.GetProperties())
            {
                if (pi.GetCustomAttributes(typeof(RpcFieldAttribute), false).Length == 0)
                    continue;
                MessageFieldMemberDescription mfmd = new MessageFieldMemberDescription();
                mfmd.Type = pi.PropertyType;
                mfmd.Name = pi.Name;
                if (mfmd.IsGenericType())
                {
                    mfmd.GenericTypeName = string.Format("List<{0}>", mfmd.GetGenericType().Name);
                }

                if (!mfmd.IsPrimitiveType())
                {
                    mfmd.ObjectIndex = ++mfd.ObjectCount;
                }

                mfd.MemberMaskSize = GetObjectMaskSize(mfd.ObjectCount);
                mfd.Members.Add(mfmd);
            }
        }

        private int GetMessageId(MethodDescription method)
        {
            UInt16 serviceid = (UInt16)RpcControl.GetHashCode(method.Interface.FullName);
            UInt16 methodid = (UInt16)RpcControl.GetHashCode(method.Method.Name);
            return MessageIdFromServiceAndMethodId(serviceid, methodid);
        }

        private int MessageIdFromServiceAndMethodId(UInt16 nServiceId, UInt16 nMethodId)
        {
            UInt32 result = nMethodId;
            result |= ((UInt32)nServiceId) << 16;
            return (int)result;
        }

        private int GetObjectMaskSize(int ObjectCount)
        {
            if (ObjectCount <= 8)
            {
                return 8;
            }
            else if (ObjectCount <= 16)
            {
                return 16;
            }
            else if (ObjectCount <= 32)
            {
                return 32;
            }
            else
            {
                return 64;
            }
        }

        private void GetSharedField(MessageDescription md)
        {
            md.SharedObjects = new List<MessageFieldDescription>();
            foreach (MessageFieldDescription mfd in md.Fields)
            {
                if (mfd.IsPrimitiveType())
                    continue;
                if (mfd.IsGenericType())
                {
                    Type genericType = mfd.GetGenericType();
                    md.SharedObjects.AddRange(GetSharedObjectByType(genericType));
                }
                else
                {
                    md.SharedObjects.AddRange(GetSharedObjectByType(mfd.Type));
                }
            }
        }

        private List<MessageFieldDescription> GetSharedObjectByType(Type type)
        {
            List<MessageFieldDescription> result = new List<MessageFieldDescription>();
            
            MessageFieldDescription mfd = CreateShareObject(type);
            if (mfd != null)
                result.Add(mfd);

            foreach (PropertyInfo pi in type.GetProperties())
            {
                if (pi.GetCustomAttributes(typeof(RpcFieldAttribute), false).Length > 0)
                {
                    if (!MemberDescription.IsPrimitiveType(pi.PropertyType))
                    {
                        if (MemberDescription.IsGenericType(pi.PropertyType))
                        {
                            Type genericType = MemberDescription.GetGenericType(pi.PropertyType);
                            result.AddRange(GetSharedObjectByType(genericType));
                        }
                        else
                        {
                            mfd = CreateShareObject(pi.PropertyType);
                            if (mfd != null)
                                result.Add(mfd);
                        }
                    }
                }
            }
            return result;
        }

        private MessageFieldDescription CreateShareObject(Type type)
        {
            MessageFieldDescription mfd = null;
            if (MessageFieldDescription.GetMessageFieldType(type) == MessageFieldType.ClientType)
            {
                mfd = new MessageFieldDescription();
                mfd.Name = "ShareObject";
                mfd.Type = type;
                CreateMembers(mfd);
            }
            return mfd;
        }
    }
}
